package com.fsy.its.netty.dtu;

import com.fsy.its.entity.Dtu;
import com.fsy.its.netty.config.DtuDecoderFactory;
import com.fsy.its.service.DtuService;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.FixedLengthFrameDecoder;
import io.netty.util.Attribute;
import io.netty.util.AttributeKey;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

@Slf4j
@Scope("prototype")
@Component
public class DtuIdDecoder extends FixedLengthFrameDecoder {

    public static final AttributeKey<Dtu> DTU_KEY = AttributeKey.valueOf("dtu.id");

    public static final int DTU_ID_LEN = 4;

    @Autowired
    private DtuDecoderFactory dtuDecoderFactory;

    @Autowired
    private DtuService dtuService;

    public DtuIdDecoder() {
        super(DTU_ID_LEN);
    }

    @Override
    protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
        //已经获取到了dtu的id，已经是透传报警主机的数据了，直接交给后面的decoder处理
        if( ctx.channel().hasAttr(DTU_KEY) ) {
            //返回副本给下一个decoder，并且跳过in的所有可读字节，否则netty会报未读任何字节的异常
            ByteBuf frame = in.retainedDuplicate();
            in.skipBytes(in.readableBytes());
            return frame;
        }

        //dtu连上服务器时，会先发送固定长度的dtu的id
        ByteBuf id = (ByteBuf)super.decode(ctx, in);
        String dtuId = ByteBufUtil.hexDump(id);

        Dtu dtu = dtuService.getDtu(dtuId);
        if( dtu == null ) {
            //不认识的dtu，关闭连接
            log.warn("Unknown dtu, id={}", dtuId);
            ctx.channel().close();
            return null;
        }
        dtu.setConnectTime(LocalDateTime.now());
        Attribute<Dtu> attr = ctx.channel().attr(DTU_KEY);
        attr.set(dtu);

        //通知handler有新的dtu连接
        ctx.fireChannelActive();

        //根据报警主机的品牌和型号找到对应的decoder
        String brand = dtu.getHostBrand();
        String model = dtu.getHostModel();
        ChannelHandler decoder = (ChannelHandler) dtuDecoderFactory.getDecoder(brand, model);
        if( decoder != null ) {
            ctx.pipeline().addBefore("handler", "decoder", decoder);
            log.info("A dtu connected, brand={}, model={}, dtuId={}, decoder={}",
                    brand, model, dtuId, decoder.getClass().getSimpleName());
        }
        else {
            //没有对应的解码器，关闭连接
            log.warn("No decoder for fire host, brand={}, model={}, dtuId={}", brand, model, dtuId);
            ctx.channel().close();
        }

        return null;
    }
}
